//
//  CSMACAVCSInitiator.cpp
//  WiFiMACSim
//
//  Created by Christopher Kemsley on 4/1/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include "CSMACAVCSInitiator.hpp"
#include "Settings.hpp"

using namespace Sim ;




namespace Sim {
	char const*	const sIdle				( "Idle" ) ;
	char const*	const sTXControl		( "TX Control" ) ;
	char const*	const sTXControlFail	( "TX Control Failed" ) ;
	char const*	const sTXReturn			( "TX Return..." ) ;
	char const*	const sTXReturnFail		( "TX Return Failed" ) ;
	char const*	const sTXData			( "TX Data" ) ;
	char const*	const sTXDataFail		( "TX Data Failed" ) ;
}





CSMACAVCSInitiator :: CSMACAVCSInitiator ( ID const & name , ID const & inTarget , UInteger inLambdaScalar )
:	TransitionalStateMachineNode<CSMACAVCSInitiator>	( name , kStateIdle )
,	target				( inTarget )
,	lambdaScalar		( inLambdaScalar )
,	message				( 0 )
,	timer				( )
,	statistics			( std::string("Node ")+name , sIdle )
	{
	timer . skip ( 0 ) ; // start immediately
	
	
	registerStateCallback ( kStateIdle , &CSMACAVCSInitiator::run_idle , "Idle. Waiting to send RTS" ) ;
	
	// Transaction control
	registerStateCallback ( kStateSendingRTS , &CSMACAVCSInitiator::run_sendingRTS , "Sending RTS" ) ;
	registerStateCallback ( kStateWaitCTSOrDifs , &CSMACAVCSInitiator::run_waitCTS_difs , "Waiting for (CTS) or (difs timeout)" ) ;
	registerStateCallback ( kStateWaitACKOrDifs , &CSMACAVCSInitiator::run_waitACK_difs , "Waiting for (ACK) or (difs timeout)" ) ;
	
	registerStateCallback ( kStateSendingData , &CSMACAVCSInitiator::run_sendingData , "Sending data" ) ;
	
	// VCS States
	registerStateCallback ( kStateVCSSawCTS , &CSMACAVCSInitiator::run_vcs_saw , "Saw CTS. Waiting for NAV" ) ;
	registerStateCallback ( kStateVCSSawRTS , &CSMACAVCSInitiator::run_vcs_saw , "Saw RTS. Waiting for NAV" ) ;
	}
CSMACAVCSInitiator :: ~CSMACAVCSInitiator ()
	{
	
	}






void	CSMACAVCSInitiator :: breakwayForVCS	( Message * incoming , bool completed , bool isBusy )
	{
	// <cts> ... + <sifs> + <data> + <sifs> + <ack>
	// <cts> isn't counted in time since we wait until the end
	UInteger	sifs			=	Settings::shared().SIFSDuration ;
	UInteger	data			=	Settings::shared().dataPacketDuration ;
	UInteger	control			=	Settings::shared().controlPacketDuration ;
	UInteger	waitDuration	=	control + data + sifs*2 ;
	
	if ( completed )
		{
		if ( incoming->type == Message::kTypeCTS )
			{
			setState ( kStateVCSSawCTS ) ;
			statistics << "Waiting (hidden tx/rx)" ;
			
			}
		else if ( incoming->type == Message::kTypeRTS )
			{
			setState ( kStateVCSSawRTS ) ;
			statistics << "Waiting (hidden tx/rx)" ;
			// <rts> + <sifs>
			waitDuration += sifs + control ;
			}
		}
	
	//std::cout << NodeStamp << "Waiting " << waitDuration << " slots\n" ;
	timer . skipNext ( waitDuration ) ;
	}










void	CSMACAVCSInitiator :: run_idle			( Message * incoming , bool completed , bool isBusy )
	{
	statistics << sIdle ;
	
	if ( incoming != 0 )
		{
		if ( incoming->target == identity )
			{
			ThrowException ( "CSMACAVCSInitiator" , "Not expecting an incoming message right now" , __FILE__,__LINE__ ) ;
			}
		else
			{
			breakwayForVCS ( incoming , completed , isBusy ) ;
			}
		}
	else
		{
		if ( timer.isReset() )
			{
			timer . skip ( Settings::shared().poissonSlotCount(lambdaScalar) ) ;
			}
		else if ( timer.isTime() )
			{
			setState ( kStateSendingRTS ) ;
			statistics << sTXControl ;
			
			UInteger	duration	=	Settings::shared().controlPacketDuration ;
			
			message = new Message ( identity , target , Message::kTypeRTS , duration ) ;
			Medium::shared() . registerMessage ( message ) ;
			}
		}
	}


void	CSMACAVCSInitiator :: run_sendingRTS	( Message * incoming , bool completed , bool isBusy )
	{
	// Wait until our message is done
	if ( completed )
		{
		/* Statistics */
		if ( message -> status != Message::kStatusSuccessful )
			{
			//std::cout << NodeStamp << "RTS Message was never received/handled\n" ;
			statistics <= sTXControlFail ;
			}
		/* Statistics */
		
		message -> release() ;
		message = 0 ;
		
		timer . skipNext ( Settings::shared().DIFSDuration ) ;
		setState ( kStateWaitCTSOrDifs ) ;
		statistics << sTXReturn ;
		}
	}
void	CSMACAVCSInitiator :: run_waitCTS_difs	( Message * incoming , bool completed , bool isBusy )
	{
	if ( incoming != 0 )
		{
		if ( incoming->target == identity )
			{
			if ( completed )
				{
				Message & m	=	*incoming ;
				
				if ( m.type != Message::kTypeCTS )
					ThrowException ( "CSMACAVCSInitiator" , "Receieved non-CTS after RTS" , __FILE__,__LINE__ ) ;
				
				setState ( kStateSendingData ) ;
				statistics << sTXData ;
				
				message = new Message ( identity , target , Message::kTypeData , Settings::shared().dataPacketDuration ) ;
				Medium::shared() . registerMessage ( message ) ;
				}
			else
				{
				incoming->status = Message::kStatusSuccessful ;
				}
			}
		}
	else
		{
		if ( timer.isTime() )
			{
			/* Statistics */
			//std::cout << NodeStamp << "Timed out waiting for CTS\n" ;
			statistics <= sTXReturnFail ;
			/* Statistics */
			
			setState ( kStateIdle ) ;
			}
		}
	}


void	CSMACAVCSInitiator :: run_sendingData	( Message * incoming , bool completed , bool isBusy )
	{
	if ( completed )
		{
		/* Statistics */
		if ( message -> status != Message::kStatusSuccessful )
			{
			//std::cout << NodeStamp << "Data Message was never received/handled\n" ;
			statistics <= sTXDataFail ;
			}
		/* Statistics */
		
		message -> release() ;
		message = 0 ;
		
		timer . skipNext ( Settings::shared().DIFSDuration ) ;
		setState ( kStateWaitACKOrDifs ) ;
		statistics << sTXReturn ;
		}
	}

void	CSMACAVCSInitiator :: run_waitACK_difs	( Message * incoming , bool completed , bool isBusy )
	{
	if ( incoming != 0 )
		{
		if ( incoming->target == identity )
			{
			if ( completed )
				{
				Message & m	=	*incoming ;
				
				if ( m.type != Message::kTypeACK )
					ThrowException ( "CSMACAVCSInitiator" , "Receieved non-ACK after DATA" , __FILE__,__LINE__ ) ;
				
				timer . reset () ;
				setState ( kStateIdle ) ;
				statistics << sIdle ;
				}
			else
				{
				incoming->status = Message::kStatusSuccessful ;
				}
			}
		}
	else
		{
		if ( timer.isTime() )
			{
			/* Statistics */
			//std::cout << NodeStamp << "Timed out waiting for CTS\n" ;
			statistics <= sTXReturnFail ;
			/* Statistics */
			
			setState ( kStateIdle ) ;
			statistics << sIdle ;
			}
		}
	}



void	CSMACAVCSInitiator :: run_vcs_saw	( Message * incoming , bool completed , bool isBusy )
	{
	if ( timer.isTime() )
		setState ( kStateIdle ) ;
	}

void	CSMACAVCSInitiator :: run_waitDifs	( Message * incoming , bool completed , bool isBusy )
	{
	if ( timer.isTime() )
		setState ( kStateIdle ) ;
	}

